package com.hancai.config.filter;

import com.hancai.constant.Constants;
import com.hancai.model.TUser;
import com.hancai.result.CodeEnum;
import com.hancai.result.R;
import com.hancai.service.RedisService;
import com.hancai.util.JSONUtils;
import com.hancai.util.JWTUtils;
import com.hancai.util.ResponseUtils;
import jakarta.annotation.Resource;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;
import java.util.concurrent.TimeUnit;

/**
 * @author 涵菜
 * @version 1.0
 */
@Component
public class TokenVerifyFilter extends OncePerRequestFilter {
    @Resource
    private RedisService redisService;

    //springboot框架的ioc容器中已经创建好了该线程池，可以注入直接使用
    @Resource
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;

    /**
     *定义具体的过滤逻辑。
     * @param request 当前请求对象
     * @param response 当前响应对象
     * @param filterChain 过滤器链，用于将请求传递给下一个过滤器或目标资源。
     * @throws ServletException
     * @throws IOException
     */
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        //如果是登录请求，此时还没有生成jwt,那不需要对登录请求进行jwt验证
        if(request.getRequestURI().equals(Constants.LOGIN_URI)){
            filterChain.doFilter(request,response);
        }else {
            String token=request.getHeader("Authorization");
            //StringUtils.hasText 用于判断字符串是否为非空且包含有效字符。
            //如果token 是无效的（null、空字符串或仅包含空白字符）。
            if(!StringUtils.hasText(token)){
                //登录失败的统一结果
                R result=R.FAIL(CodeEnum.TOKEN_IS_EMPTY);
                //把R对象转成json
                String resultJSON= JSONUtils.toJSON(result);
                ResponseUtils.write(response,resultJSON);
                return;
            }
            //在解析之前先验证一下token在前端有没有被篡改过-如果验证没有通过
            if (!JWTUtils.verifyJWT(token)) {
                R result=R.FAIL(CodeEnum.TOKEN_IS_ERROR);
                String resultJSON = JSONUtils.toJSON(result);
                ResponseUtils.write(response,resultJSON);
                return;
            }
            TUser tUser= JWTUtils.parseUserFromJWT(token);
            String redisToken=(String)redisService.getValue(Constants.REDIS_JWT_KEY+tUser.getId());
            if(!StringUtils.hasText(redisToken)){
                R result=R.FAIL(CodeEnum.TOKEN_IS_EXPIRED);
                String resultJSON = JSONUtils.toJSON(result);
                ResponseUtils.write(response,resultJSON);
                return;
            }
            if(!token.equals(redisToken)){
                R result=R.FAIL(CodeEnum.TOKEN_NOT_MATCH);
                String resultJSON=JSONUtils.toJSON(result);
                ResponseUtils.write(response,resultJSON);
                return;
            }
            //jwt验证通过了，那么在spring security的上下文环境中要设置一下，设置当前
            //这个用户是登录过的，后续就不要再拦截了
            UsernamePasswordAuthenticationToken authenticationToken=
                    new UsernamePasswordAuthenticationToken(tUser,tUser.getLoginPwd(),tUser.getAuthorities());
            //它将用户的认证信息（Authentication 对象）存储到 SecurityContext 中，从而实现用户会话的管理。
            SecurityContextHolder.getContext().setAuthentication(authenticationToken);
            //异步处理，不影响后续代码执行，使用线程池去执行
            threadPoolTaskExecutor.execute(()->{
                //刷新token(异步new一个线程去执行)
                String rememberMe=request.getHeader("rememberMe");
                if(Boolean.parseBoolean(rememberMe)){
                    redisService.expire(Constants.REDIS_JWT_KEY+tUser.getId(),Constants.EXPIRE_TIME, TimeUnit.SECONDS);
                }else {
                    redisService.expire(Constants.REDIS_JWT_KEY+tUser.getId(),Constants.DEFAULT_EXPIRE_TIME,TimeUnit.SECONDS);
                }
            });
            //验证jwt通过了，让filter继续执行下一个filter
            filterChain.doFilter(request,response);
        }
    }
}
